/*
 * Copyright (c) 2013-2015 Charkey. All rights reserved.
 *
 * This software is the confidential and proprietary information of Charkey.
 * You shall not disclose such Confidential Information and shall use it only
 * in accordance with the terms of the agreements you entered into with Charkey.
 *
 * Charkey MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
 *
 * Charkey SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING,
 * MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 */

package cn.simastudio.talos.common.utils;

import cn.simastudio.talos.common.utils.file.SimaFileUtils;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import net.coobird.thumbnailator.Thumbnails;
import net.coobird.thumbnailator.geometry.Positions;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URL;
import java.util.Hashtable;

/**
 * Usage:二维码生成工具类
 *
 * @author Labaijos
 * @date 2015/5/21 22:47
 */
public class QRCodeUtils {

    /**
     * 编码
     */
    private static final String CHARSET = "utf-8";

    /**
     * 文件类型
     */
    private static final String FORMAT_NAME = "JPG";

    /**
     * 二维码尺寸
     */
    public static int QRCODE_SIZE = 960;

    /**
     * 创建二维码图片
     *
     * @param content    二维码内容
     * @param qrCodeSize 二维码尺寸
     * @return BufferedImage 二维码图像缓冲区
     * @throws Exception
     */
    private static BufferedImage createImage(String content, int qrCodeSize) throws Exception {
        Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
        hints.put(EncodeHintType.MARGIN, 1);
        BitMatrix bitMatrix = new MultiFormatWriter()
                .encode(content, BarcodeFormat.QR_CODE, qrCodeSize, qrCodeSize, hints);
        int width = bitMatrix.getWidth();
        int height = bitMatrix.getHeight();
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
            }
        }
        return image;
    }

    /**
     * 生成二维码(内嵌LOGO) 输出流
     *
     * @param content      内容
     * @param qrCodeSize   二维码尺寸
     * @param logoUrl      logo地址
     * @param qrCodeOutput 输出流
     * @param needCompress 是否压缩logo
     * @throws Exception
     */
    public static void encode(String content, int qrCodeSize, String logoUrl, ByteArrayOutputStream qrCodeOutput,
                              boolean needCompress) throws Exception {
        // 创建二维码头像
        BufferedImage image = createImage(content, qrCodeSize);
        ByteArrayOutputStream outBuff = new ByteArrayOutputStream();
        ImageIO.write(image, FORMAT_NAME, outBuff);

        // 插入头像
        if (logoUrl != null && !logoUrl.isEmpty()) {
            // 头像压缩输出流
            URL url = new URL(logoUrl);
            InputStream logoInput = new BufferedInputStream(url.openStream());
            // 压缩头像
            if (needCompress) {
                ByteArrayOutputStream logoOutStream = new ByteArrayOutputStream();
                Thumbnails.of(logoInput).size(qrCodeSize / 5, qrCodeSize / 5).keepAspectRatio(false).toOutputStream(logoOutStream);
                logoInput = new ByteArrayInputStream(logoOutStream.toByteArray());
            }

            // 添加头像水印
            ByteArrayInputStream qrCodeInputStream = new ByteArrayInputStream(outBuff.toByteArray());
            Thumbnails.of(qrCodeInputStream).size(qrCodeSize, qrCodeSize)
                    .watermark(Positions.CENTER, ImageIO.read(logoInput), 1f).outputQuality(1f)
                    .toOutputStream(qrCodeOutput);
            logoInput.close();
            qrCodeInputStream.close();
        }
    }

    /**
     * 生成二维码 输出流
     *
     * @param content 内容
     * @param output  输出流
     * @throws Exception
     */
    public static void encode(String content, OutputStream output) throws Exception {
        BufferedImage image = createImage(content, QRCODE_SIZE);
        ImageIO.write(image, FORMAT_NAME, output);
    }

    /**
     * 生成二维码(内嵌LOGO) 文件
     *
     * @param content      内容
     * @param qrCodeSize   二维码尺寸
     * @param logoUrl      logo地址
     * @param destPath     二维码地址
     * @param needCompress 是否压缩logo
     * @throws Exception
     */
    public static void encode(String content, int qrCodeSize, String logoUrl, String destPath, boolean needCompress) throws Exception {
        // 创建二维码头像
        BufferedImage image = createImage(content, qrCodeSize);
        SimaFileUtils.mkdirs(destPath);
        // 插入头像
        if (logoUrl != null && !logoUrl.isEmpty()) {
            ByteArrayOutputStream outBuff = new ByteArrayOutputStream();
            ImageIO.write(image, FORMAT_NAME, outBuff);
            // 头像压缩输出流
            URL url = new URL(logoUrl);
            InputStream logoInput = new BufferedInputStream(url.openStream());
            // 压缩头像
            if (needCompress) {
                ByteArrayOutputStream logoOutStream = new ByteArrayOutputStream();
                Thumbnails.of(logoInput).size(qrCodeSize / 5, qrCodeSize / 5).keepAspectRatio(false).toOutputStream(logoOutStream);
                logoInput = new ByteArrayInputStream(logoOutStream.toByteArray());
            }

            // 添加头像水印
            ByteArrayInputStream qrcodeInputStream = new ByteArrayInputStream(outBuff.toByteArray());
            Thumbnails.of(qrcodeInputStream).size(qrCodeSize, qrCodeSize)
                    .watermark(Positions.CENTER, ImageIO.read(logoInput), 1f).outputQuality(1f)
                    .toFile(destPath);
            logoInput.close();
            qrcodeInputStream.close();
        } else {
            ImageIO.write(image, FORMAT_NAME, new File(destPath));
        }
    }

    /**
     * 生成二维码(内嵌LOGO) 文件不带尺寸，压缩
     *
     * @param content  内容
     * @param destPath 目标路径
     * @param logoUrl  logo路径
     * @throws Exception
     */
    public static void encode(String content, String destPath, String logoUrl) throws Exception {
        encode(content, QRCODE_SIZE, logoUrl, destPath, true);
    }

    /**
     * 生成二维码(内嵌LOGO) 文件 带尺寸，压缩
     *
     * @param content    内容
     * @param qrCodeSize 尺寸
     * @param destPath   目标路径
     * @param logoUrl    logo路径
     * @throws Exception
     */
    public static void encode(String content, int qrCodeSize, String destPath, String logoUrl) throws Exception {
        encode(content, qrCodeSize, logoUrl, destPath, true);
    }

    /**
     * 生成二维码 文件
     *
     * @param content  内容
     * @param destPath 存储地址
     * @throws Exception
     */
    public static void encode(String content, String destPath) throws Exception {
        encode(content, QRCODE_SIZE, null, destPath, false);
    }

    /**
     * 生成二维码 文件 带尺寸
     *
     * @param content    内容
     * @param qrCodeSize 二维码尺寸
     * @param destPath   目标路径
     * @throws Exception
     */
    public static void encode(String content, int qrCodeSize, String destPath) throws Exception {
        encode(content, qrCodeSize, null, destPath, false);
    }

}
